/*
    Title:  i2c library for 6050 sensor and others
	Purpose: test microchip i2c hardware library
	by: Rolf Ziegler
	Date: July 2010
  
	Input: RB0+RB1 of pic 18f4550
	Oupout: Subroutine returning 1 word with temperature of chip
			or temperature in 1/100 deg.
*/


//#define SENSOR_ADDRESS 0x9E

#include <p18cxxx.h>
#include <i2c.h>

#include <delays.h>
#include "typedef.h"
#include "i2clib.h"

#define  SDA_IN     TRISBbits.TRISB0 = 1 // define macro for data pin output
#define  SCL_IN    TRISBbits.TRISB1 = 1 // define macro for data pin input


void delayi2c(void)
{  //delay between i2c writes
 //Delay10TCYx(25); //delays 250 clock cycles = 50us delay at 20MHz clock
 Delay10TCYx(50); //delays 500 clock cycles = 41us delay at 48MHz clock
}

signed char WriteI2C( unsigned char data_out );

void i2cInit(void) {

   SDA_IN;
   SCL_IN;

   OpenI2C(MASTER,SLEW_OFF);

   SSPSTAT=0x80;

#define MCUCLK 48000 // khz
#define I2C_SPEED 400 // khz

   SSPADD = ( ( MCUCLK / ( 4 * I2C_SPEED ) ) - 4 ) ;

}
   

u8 i2cReady(void)
{
   u8 ack;
//   LedR=0;
   StartI2C();            // If the write command is acknowledged,
   ack = WriteI2C(ACCEL);  // then the device is ready.
   StopI2C();
   return !ack;
//   LedR=1;
}


void i2cWriteByte(u8 device,u8 address, u8 data)
{
//   while(!ds1731_rdy());
   StartI2C();
   IdleI2C();
   WriteI2C(device);
   IdleI2C();
   WriteI2C(address);
   IdleI2C();
   WriteI2C(data);
   IdleI2C();
   StopI2C();
}


u8 i2cReadByte(u8 device,u8 address)
{
   u8 data;

   StartI2C();
   IdleI2C();
   WriteI2C(device);
   IdleI2C();
   WriteI2C(address);
   IdleI2C();
   RestartI2C();
   IdleI2C();
   WriteI2C(device+1);
   IdleI2C();
   data=ReadI2C();
   IdleI2C();
   StopI2C();
   return(data);
}

signed char getsI2C( unsigned char *rdptr, unsigned char length )
{
    while ( length -- )            // perform getcI2C2() for 'length' number of bytes
    {
      *rdptr++ = getcI2C();       // save byte received
      while ( SSPCON2bits.RCEN ); // check that receive sequence is over

  if ( PIR2bits.BCLIF )           // test for bus collision
      {
        return ( -1 );             // return with Bus Collision error
      }

	if( ((SSPCON1&0x0F)==0x08) || ((SSPCON1&0x0F)==0x0B) )	//master mode only
	{
      if ( length )               // test if 'length' bytes have been read
      {
        SSPCON2bits.ACKDT = 0;    // set acknowledge bit state for ACK
        SSPCON2bits.ACKEN = 1;    // initiate bus acknowledge sequence
        while ( SSPCON2bits.ACKEN ); // wait until ACK sequence is over
      }
	}

    }
    return ( 0 );                  // last byte received so don't send ACK
}

signed char I2CReadArray( unsigned char control, unsigned char address, unsigned char *rdptr, unsigned char length )
{
  IdleI2C();                      // ensure module is idle
  StartI2C();                     // initiate START condition
  while ( SSPCON2bits.SEN );      // wait until start condition is over
  if ( PIR2bits.BCLIF )           // test for bus collision
  {
    return ( -1 );                 // return with Bus Collision error
  }
  else
  {
    if ( WriteI2C( control ) )    // write 1 byte
    {
	  StopI2C();
      return ( -3 );               // set error for write collision
    }

    //IdleI2C1();                    // ensure module is idle
    if ( !SSPCON2bits.ACKSTAT )   // test for ACK condition, if received
    {
      if ( WriteI2C( address ) )  // WRITE word address to EEPROM
      {
	    StopI2C();
        return ( -3 );             // return with write collision error
      }

      //IdleI2C1();                  // ensure module is idle
      if ( !SSPCON2bits.ACKSTAT ) // test for ACK condition received
      {
        RestartI2C();             // generate I2C bus restart condition
        while ( SSPCON2bits.RSEN );  // wait until re-start condition is over
        if ( WriteI2C( control +1 ) )// WRITE 1 byte - R/W bit should be 1 for read
        {
		  StopI2C();
          return ( -3 );           // set error for write collision
        }

        //IdleI2C1();                // ensure module is idle
        if ( !SSPCON2bits.ACKSTAT )// test for ACK condition received
        {
          if ( getsI2C( rdptr, length ) )// read in multiple bytes
          {
            return ( -1 );         // return with Bus Collision error
          }

          NotAckI2C();            // send not ACK condition
          while ( SSPCON2bits.ACKEN );// wait until ACK sequence is over
          StopI2C();              // send STOP condition
          while ( SSPCON2bits.PEN );// wait until stop condition is over
          if ( PIR2bits.BCLIF )   // test for bus collision
          {
            return ( -1 );         // return with Bus Collision error
          }
        }
        else
        {
		  StopI2C();
          return ( -2 );           // return with Not Ack error
        }
      }
      else
      {
	    StopI2C();
        return ( -2 );             // return with Not Ack error
      }
    }
    else
    {
	  StopI2C();
      return ( -2 );               // return with Not Ack error
    }
  }
  return ( 0 );                    // return with no error
}

u16 i2cReadWord(u8 device,u8 address)
{
   int data;

   StartI2C();
   IdleI2C();
   WriteI2C(device);
   IdleI2C();
   WriteI2C(address);
   IdleI2C();
   RestartI2C();
   IdleI2C();
   WriteI2C(device+1);
   IdleI2C();
   data=(int)ReadI2C()<<8;
   AckI2C();
   IdleI2C();
   data+=(int)ReadI2C();
   NotAckI2C();
   IdleI2C();
   StopI2C();
   return((int)data);
}


signed char WriteI2C( unsigned char data_out )
{
  SSPBUF = data_out;           // write single byte to SSPBUF
  if ( SSPCON1bits.WCOL )      // test if write collision occurred
   return ( -1 );              // if WCOL bit is set return negative #
  else
  {
	if( ((SSPCON1&0x0F)!=0x08) && ((SSPCON1&0x0F)!=0x0B) )	//Slave mode only
	{
	      SSPCON1bits.CKP = 1;        // release clock line
	      while ( !PIR1bits.SSPIF );  // wait until ninth clock pulse received

	      if ( ( !SSPSTATbits.R_W ) && ( !SSPSTATbits.BF ) )// if R/W=0 and BF=0, NOT ACK was received
	      {
	        return ( -2 );           //return NACK
	      }
		  else
		  {
			return ( 0 );				//return ACK
		  }
	}
	else if( ((SSPCON1&0x0F)==0x08) || ((SSPCON1&0x0F)==0x0B) )	//master mode only
	{
	    while( SSPSTATbits.BF );   // wait until write cycle is complete
	    IdleI2C();                 // ensure module is idle
	    if ( SSPCON2bits.ACKSTAT ) // test for ACK condition received
	    	 return ( -2 );			// return NACK
		else return ( 0 );              //return ACK
	}

  }
}


////////////////////// end of driver file /////////////////////////////////////
